﻿#include "BasePass.h"

#include "../../../Common/Singleton.h"
#include "../ResourceManager/DX3GeometryManager.h"
#include "../ResourceManager/DX3MaterailManager.h"
#include "../ResourceManager/DX3RootSingtureManager.h"
#include "../ResourceManager/DX3ShadersAndInputLayoutManager.h"

using namespace  DirectX;

BasePass::BasePass()
{
}

void BasePass::BuildRenderItems(std::vector<std::unique_ptr<RenderItem>>& allRitems, UINT& lastObjIndex)
{
    auto boxRitem = std::make_unique<RenderItem>();
	XMStoreFloat4x4(&boxRitem->World, XMMatrixScaling(2.0f, 2.0f, 2.0f)*XMMatrixTranslation(0.0f, 1.0f, 0.0f));
	XMStoreFloat4x4(&boxRitem->TexTransform, XMMatrixScaling(1.0f, 1.0f, 1.0f));
	boxRitem->ObjCBIndex = lastObjIndex;
	lastObjIndex ++;
	
	boxRitem->Mat = Singleton<DX3MaterailManager>::Instance()->GetMaterial("crate0").get();
	boxRitem->Geo = Singleton<DX3GeometryManager>::Instance()->GetGeometry("shapeGeo").get();
	boxRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
	boxRitem->IndexCount = boxRitem->Geo->DrawArgs["box"].IndexCount;
	boxRitem->StartIndexLocation = boxRitem->Geo->DrawArgs["box"].StartIndexLocation;
	boxRitem->BaseVertexLocation = boxRitem->Geo->DrawArgs["box"].BaseVertexLocation;

	mRenderItems.push_back(boxRitem.get());
	allRitems.push_back(std::move(boxRitem));

    auto gridRitem = std::make_unique<RenderItem>();
    gridRitem->World = MathHelper::Identity4x4();
	XMStoreFloat4x4(&gridRitem->TexTransform, XMMatrixScaling(8.0f, 8.0f, 1.0f));
	gridRitem->ObjCBIndex = lastObjIndex;
	lastObjIndex ++;
	
	gridRitem->Mat = Singleton<DX3MaterailManager>::Instance()->GetMaterial("tile0").get();
	gridRitem->Geo = Singleton<DX3GeometryManager>::Instance()->GetGeometry("shapeGeo").get();
	gridRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
    gridRitem->IndexCount = gridRitem->Geo->DrawArgs["grid"].IndexCount;
    gridRitem->StartIndexLocation = gridRitem->Geo->DrawArgs["grid"].StartIndexLocation;
    gridRitem->BaseVertexLocation = gridRitem->Geo->DrawArgs["grid"].BaseVertexLocation;
	mRenderItems.push_back(gridRitem.get());
	allRitems.push_back(std::move(gridRitem));

	XMMATRIX brickTexTransform = XMMatrixScaling(1.0f, 1.0f, 1.0f);
	for(int i = 0; i < 5; ++i)
	{
		auto leftCylRitem = std::make_unique<RenderItem>();
		auto rightCylRitem = std::make_unique<RenderItem>();
		auto leftSphereRitem = std::make_unique<RenderItem>();
		auto rightSphereRitem = std::make_unique<RenderItem>();

		XMMATRIX leftCylWorld = XMMatrixTranslation(-5.0f, 1.5f, -10.0f + i*5.0f);
		XMMATRIX rightCylWorld = XMMatrixTranslation(+5.0f, 1.5f, -10.0f + i*5.0f);

		XMMATRIX leftSphereWorld = XMMatrixTranslation(-5.0f, 3.5f, -10.0f + i*5.0f);
		XMMATRIX rightSphereWorld = XMMatrixTranslation(+5.0f, 3.5f, -10.0f + i*5.0f);

		XMStoreFloat4x4(&leftCylRitem->World, rightCylWorld);
		XMStoreFloat4x4(&leftCylRitem->TexTransform, brickTexTransform);
		leftCylRitem->ObjCBIndex = lastObjIndex;
		lastObjIndex ++;
		
		leftCylRitem->Mat = Singleton<DX3MaterailManager>::Instance()->GetMaterial("bricks0").get();
		leftCylRitem->Geo = Singleton<DX3GeometryManager>::Instance()->GetGeometry("shapeGeo").get();
		leftCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		leftCylRitem->IndexCount = leftCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
		leftCylRitem->StartIndexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
		leftCylRitem->BaseVertexLocation = leftCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

		XMStoreFloat4x4(&rightCylRitem->World, leftCylWorld);
		XMStoreFloat4x4(&rightCylRitem->TexTransform, brickTexTransform);
		rightCylRitem->ObjCBIndex = lastObjIndex;
		lastObjIndex++;
		
		rightCylRitem->Mat = Singleton<DX3MaterailManager>::Instance()->GetMaterial("bricks0").get();
		rightCylRitem->Geo = Singleton<DX3GeometryManager>::Instance()->GetGeometry("shapeGeo").get();
		rightCylRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		rightCylRitem->IndexCount = rightCylRitem->Geo->DrawArgs["cylinder"].IndexCount;
		rightCylRitem->StartIndexLocation = rightCylRitem->Geo->DrawArgs["cylinder"].StartIndexLocation;
		rightCylRitem->BaseVertexLocation = rightCylRitem->Geo->DrawArgs["cylinder"].BaseVertexLocation;

		XMStoreFloat4x4(&leftSphereRitem->World, leftSphereWorld);
		leftSphereRitem->TexTransform = MathHelper::Identity4x4();
		leftSphereRitem->ObjCBIndex = lastObjIndex;
		lastObjIndex++;
		
		leftSphereRitem->Mat = Singleton<DX3MaterailManager>::Instance()->GetMaterial("stone0").get();
		leftSphereRitem->Geo = Singleton<DX3GeometryManager>::Instance()->GetGeometry("shapeGeo").get();
		leftSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		leftSphereRitem->IndexCount = leftSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
		leftSphereRitem->StartIndexLocation = leftSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
		leftSphereRitem->BaseVertexLocation = leftSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

		XMStoreFloat4x4(&rightSphereRitem->World, rightSphereWorld);
		rightSphereRitem->TexTransform = MathHelper::Identity4x4();
		rightSphereRitem->ObjCBIndex = lastObjIndex;
		lastObjIndex++;
		
		rightSphereRitem->Mat = Singleton<DX3MaterailManager>::Instance()->GetMaterial("stone0").get();
		rightSphereRitem->Geo = Singleton<DX3GeometryManager>::Instance()->GetGeometry("shapeGeo").get();
		rightSphereRitem->PrimitiveType = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
		rightSphereRitem->IndexCount = rightSphereRitem->Geo->DrawArgs["sphere"].IndexCount;
		rightSphereRitem->StartIndexLocation = rightSphereRitem->Geo->DrawArgs["sphere"].StartIndexLocation;
		rightSphereRitem->BaseVertexLocation = rightSphereRitem->Geo->DrawArgs["sphere"].BaseVertexLocation;

		mRenderItems.push_back(leftCylRitem.get());
		mRenderItems.push_back(rightCylRitem.get());
		mRenderItems.push_back(leftSphereRitem.get());
		mRenderItems.push_back(rightSphereRitem.get());
			
		allRitems.push_back(std::move(leftCylRitem));
		allRitems.push_back(std::move(rightCylRitem));
		allRitems.push_back(std::move(leftSphereRitem));
		allRitems.push_back(std::move(rightSphereRitem));
	}
}

void BasePass::BuildPSOs(ID3D12Device* device, DXGI_FORMAT BackBufferFormat, DXGI_FORMAT DepthStencilFormat,
    bool _4xMsaaState, UINT _4xMsaaQuality)
{
	D3D12_GRAPHICS_PIPELINE_STATE_DESC opaquePsoDesc;

	//
	// PSO for opaque objects.
	//
	ZeroMemory(&opaquePsoDesc, sizeof(D3D12_GRAPHICS_PIPELINE_STATE_DESC));
	opaquePsoDesc.InputLayout = { Singleton<DX3ShadersAndInputLayoutManager>::Instance()->GetInputLayout().data(), (UINT)Singleton<DX3ShadersAndInputLayoutManager>::Instance()->GetInputLayout().size() };
	opaquePsoDesc.pRootSignature = Singleton<DX3RootSingtureManager>::Instance()->GetRootSignature(RootSignatureType::Default).Get();
	opaquePsoDesc.VS = 
	{ 
		reinterpret_cast<BYTE*>(Singleton<DX3ShadersAndInputLayoutManager>::Instance()->GetShader("standardVS")->GetBufferPointer()), 
		Singleton<DX3ShadersAndInputLayoutManager>::Instance()->GetShader("standardVS")->GetBufferSize()
	};
	opaquePsoDesc.PS = 
	{ 
		reinterpret_cast<BYTE*>(Singleton<DX3ShadersAndInputLayoutManager>::Instance()->GetShader("opaquePS")->GetBufferPointer()),
		Singleton<DX3ShadersAndInputLayoutManager>::Instance()->GetShader("opaquePS")->GetBufferSize()
	};
	opaquePsoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
	opaquePsoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
	opaquePsoDesc.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
	opaquePsoDesc.SampleMask = UINT_MAX;
	opaquePsoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
	opaquePsoDesc.NumRenderTargets = 1;
	opaquePsoDesc.RTVFormats[0] = BackBufferFormat;
	opaquePsoDesc.SampleDesc.Count = _4xMsaaState ? 4 : 1;
	opaquePsoDesc.SampleDesc.Quality = _4xMsaaState ? (_4xMsaaQuality - 1) : 0;
	opaquePsoDesc.DSVFormat = DepthStencilFormat;
	ThrowIfFailed(device->CreateGraphicsPipelineState(&opaquePsoDesc, IID_PPV_ARGS(&mPso)));
}

void BasePass::Draw(ID3D12GraphicsCommandList* cmdList, CD3DX12_GPU_DESCRIPTOR_HANDLE hGpuHandler, UINT srvHeapSize,
    UINT objCBByteSize, UINT matCBByteSize, UINT passCBByteSize, ID3D12Resource* objectCB, ID3D12Resource* matCB,
    ID3D12Resource* passCB, const PassConstants& passValue, ID3D12Resource* OutColorRenderTarget,
    D3D12_CPU_DESCRIPTOR_HANDLE OutColorRenderTargetView, D3D12_CPU_DESCRIPTOR_HANDLE OutDepthStentilView)
{
	// Clear the back buffer and depth buffer.
	cmdList->ClearRenderTargetView(OutColorRenderTargetView, Colors::LightSteelBlue, 0, nullptr);
	cmdList->ClearDepthStencilView(OutDepthStentilView, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr);

	auto colorBufferView = OutColorRenderTargetView;
	auto depthStencilBufferView = OutDepthStentilView;
	// Specify the buffers we are going to render to.
	cmdList->OMSetRenderTargets(1, &colorBufferView, true, &depthStencilBufferView);

	cmdList->SetPipelineState(mPso.Get());
	cmdList->SetGraphicsRootSignature(Singleton<DX3RootSingtureManager>::Instance()->GetRootSignature(RootSignatureType::Default).Get());

	cmdList->SetGraphicsRootConstantBufferView(1, passCB->GetGPUVirtualAddress());
	cmdList->SetGraphicsRootShaderResourceView(2, matCB->GetGPUVirtualAddress());
	cmdList->SetGraphicsRootDescriptorTable(3, hGpuHandler);
	
	DrawRenderItem(cmdList,hGpuHandler,srvHeapSize,objCBByteSize,matCBByteSize,objectCB,matCB,mRenderItems);
}

void BasePass::DrawRenderItem(ID3D12GraphicsCommandList* cmdList, CD3DX12_GPU_DESCRIPTOR_HANDLE hGpuHandler,
	UINT srvHeapSize, UINT objCBByteSize, UINT matCBByteSize, ID3D12Resource* objectCB, ID3D12Resource* matCB,
	const std::vector<RenderItem*>& ritems)
{
	for(size_t i = 0 ; i < ritems.size(); ++i)
	{
		auto ri = ritems[i];
		auto vertexView = ri->Geo->VertexBufferView();
		auto indexView = ri->Geo->IndexBufferView();
		
		cmdList->IASetVertexBuffers(0,1,&vertexView);
		cmdList->IASetIndexBuffer(&indexView);
		cmdList->IASetPrimitiveTopology(ri->PrimitiveType);
		
		D3D12_GPU_VIRTUAL_ADDRESS objCBAddress = objectCB->GetGPUVirtualAddress() + ri->ObjCBIndex*objCBByteSize;
	
		cmdList->SetGraphicsRootConstantBufferView(0, objCBAddress);
	
		cmdList->DrawIndexedInstanced(ri->IndexCount,1,ri->StartIndexLocation,ri->BaseVertexLocation,0);
	}
}
